package com.jl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * mybatisplus查询条件构造器
 */
public class JLWrapper {

    public final static String EQ = "eq", LT = "lt", GT = "gt", LE = "le", GE = "ge", LIKE = "like", NE = "ne", IN = "in", NOT_IN = "notIn";

    /**
     * 查询构造器
     *
     * @param paramEntity 参数对象
     * @param <T>
     * @return
     */
    public <T> QueryWrapperOper<T> queryWrapper(T paramEntity) {
        return new QueryWrapperOper<>(paramEntity, EQ);
    }

    /**
     * 查询构造器
     *
     * @param paramEntity 参数对象
     * @param resultClass 构造器泛型
     * @param <T>
     * @return
     */
    public <T> QueryWrapperOper<T> queryWrapper(Object paramEntity, Class<T> resultClass) {
        return new QueryWrapperOper<>(paramEntity, resultClass, EQ);
    }

    /**
     * 查询构造器
     *
     * @param paramEntity 参数对象
     * @param <T>
     * @return
     */
    public <T> QueryWrapperOper<T> eqWrapper(T paramEntity) {
        return new QueryWrapperOper<>(paramEntity, EQ);
    }

    /**
     * 查询构造器
     *
     * @param paramEntity 参数对象
     * @param resultClass 构造器泛型
     * @param <T>
     * @return
     */
    public <T> QueryWrapperOper<T> eqWrapper(Object paramEntity, Class<T> resultClass) {
        return new QueryWrapperOper<>(paramEntity, resultClass, EQ);
    }

    /**
     * 查询构造器
     *
     * @param paramEntity 参数对象
     * @param <T>
     * @return
     */
    public <T> QueryWrapperOper<T> ltWrapper(T paramEntity) {
        return new QueryWrapperOper<>(paramEntity, LT);
    }

    /**
     * 查询构造器
     *
     * @param paramEntity 参数对象
     * @param resultClass 构造器泛型
     * @param <T>
     * @return
     */
    public <T> QueryWrapperOper<T> ltWrapper(Object paramEntity, Class<T> resultClass) {
        return new QueryWrapperOper<>(paramEntity, resultClass, LT);
    }

    /**
     * 查询构造器
     *
     * @param paramEntity 参数对象
     * @param <T>
     * @return
     */
    public <T> QueryWrapperOper<T> gtWrapper(T paramEntity) {
        return new QueryWrapperOper<>(paramEntity, GT);
    }

    /**
     * 查询构造器
     *
     * @param paramEntity 参数对象
     * @param resultClass 构造器泛型
     * @param <T>
     * @return
     */
    public <T> QueryWrapperOper<T> gtWrapper(Object paramEntity, Class<T> resultClass) {
        return new QueryWrapperOper<>(paramEntity, resultClass, GT);
    }

    /**
     * 查询构造器
     *
     * @param paramEntity 参数对象
     * @param <T>
     * @return
     */
    public <T> QueryWrapperOper<T> leWrapper(T paramEntity) {
        return new QueryWrapperOper<>(paramEntity, LE);
    }

    /**
     * 查询构造器
     *
     * @param paramEntity 参数对象
     * @param resultClass 构造器泛型
     * @param <T>
     * @return
     */
    public <T> QueryWrapperOper<T> leWrapper(Object paramEntity, Class<T> resultClass) {
        return new QueryWrapperOper<>(paramEntity, resultClass, LE);
    }

    /**
     * 查询构造器
     *
     * @param paramEntity 参数对象
     * @param <T>
     * @return
     */
    public <T> QueryWrapperOper<T> geWrapper(T paramEntity) {
        return new QueryWrapperOper<>(paramEntity, GE);
    }

    /**
     * 查询构造器
     *
     * @param paramEntity 参数对象
     * @param resultClass 构造器泛型
     * @param <T>
     * @return
     */
    public <T> QueryWrapperOper<T> geWrapper(Object paramEntity, Class<T> resultClass) {
        return new QueryWrapperOper<>(paramEntity, resultClass, GE);
    }

    /**
     * 查询构造器
     *
     * @param paramEntity 参数对象
     * @param <T>
     * @return
     */
    public <T> QueryWrapperOper<T> likeWrapper(T paramEntity) {
        return new QueryWrapperOper<>(paramEntity, LIKE);
    }

    /**
     * 查询构造器
     *
     * @param paramEntity 参数对象
     * @param resultClass 构造器泛型
     * @param <T>
     * @return
     */
    public <T> QueryWrapperOper<T> likeWrapper(Object paramEntity, Class<T> resultClass) {
        return new QueryWrapperOper<>(paramEntity, resultClass, LIKE);
    }

    /**
     * 查询构造器
     *
     * @param paramEntity 参数对象
     * @param <T>
     * @return
     */
    public <T> QueryWrapperOper<T> neWrapper(T paramEntity) {
        return new QueryWrapperOper<>(paramEntity, NE);
    }

    /**
     * 查询构造器
     *
     * @param paramEntity 参数对象
     * @param resultClass 构造器泛型
     * @param <T>
     * @return
     */
    public <T> QueryWrapperOper<T> neWrapper(Object paramEntity, Class<T> resultClass) {
        return new QueryWrapperOper<>(paramEntity, resultClass, NE);
    }

    /**
     * 查询构造器
     *
     * @param paramEntity 参数对象
     * @param <T>
     * @return
     */
    public <T> QueryWrapperOper<T> inWrapper(T paramEntity) {
        return new QueryWrapperOper<>(paramEntity, IN);
    }

    /**
     * 查询构造器
     *
     * @param paramEntity 参数对象
     * @param resultClass 构造器泛型
     * @param <T>
     * @return
     */
    public <T> QueryWrapperOper<T> inWrapper(Object paramEntity, Class<T> resultClass) {
        return new QueryWrapperOper<>(paramEntity, resultClass, IN);
    }

    /**
     * 查询构造器
     *
     * @param paramEntity 参数对象
     * @param <T>
     * @return
     */
    public <T> QueryWrapperOper<T> notInWrapper(T paramEntity) {
        return new QueryWrapperOper<>(paramEntity, NOT_IN);
    }

    /**
     * 查询构造器
     *
     * @param paramEntity 参数对象
     * @param resultClass 构造器泛型
     * @param <T>
     * @return
     */
    public <T> QueryWrapperOper<T> notInWrapper(Object paramEntity, Class<T> resultClass) {
        return new QueryWrapperOper<>(paramEntity, resultClass, NOT_IN);
    }

    /**
     * 查询构造器实现类
     *
     * @param <T>
     */
    public static class QueryWrapperOper<T> {
        private Pattern humpPattern = Pattern.compile("[A-Z]");
        private List<String> notDefaultProperty = new ArrayList<>();
        private List<JLTuple.Tuple3<String, String, Object>> arithmetics = new ArrayList<>();
        private String defaults;
        private Object paramEntity;
        private Class<T> resultClass;

        public QueryWrapperOper(Object paramEntity, String defaults) {
            this.paramEntity = paramEntity;
            this.defaults = defaults;
        }

        public QueryWrapperOper(Object paramEntity, Class<T> resultClass, String defaults) {
            this.paramEntity = paramEntity;
            this.resultClass = resultClass;
            this.defaults = defaults;
        }

        /**
         * 默认
         */
        private QueryWrapper<T> defaults(Object paramEntity) {
            QueryWrapper<T> queryWrapper = new QueryWrapper<>();
            List<JLTuple.Tuple3<String, Object, Class<?>>> tuple3s = JLReflect.PropertyReflect.getProperty(paramEntity);
            List<JLTuple.Tuple3<String, String, Object>> defaultArithmetics = new ArrayList<>();
            for (JLTuple.Tuple3<String, Object, Class<?>> tuple3 : tuple3s) {
                if (notDefaultProperty.contains(tuple3.getV1())) {
                    continue;
                }
                defaultArithmetics.add(new JLTuple.Tuple3<>(tuple3.getV1(), defaults, tuple3.getV2()));
            }
            arithmetic(queryWrapper, defaultArithmetics);
            return queryWrapper;
        }

        /**
         * 默认
         */
        private QueryWrapper<T> defaults(Object paramEntity, Class<T> resultClass) {
            QueryWrapper<T> queryWrapper = new QueryWrapper<>();
            List<JLTuple.Tuple3<String, Object, Class<?>>> tuple3s = JLReflect.PropertyReflect.getProperty(paramEntity);
            List<JLTuple.Tuple3<String, Object, Class<?>>> tuple3sr = JLReflect.PropertyReflect.getProperty(resultClass);
            List<JLTuple.Tuple3<String, String, Object>> defaultArithmetics = new ArrayList<>();
            for (JLTuple.Tuple3<String, Object, Class<?>> tuple3 : tuple3s) {
                String property = tuple3.getV1();
                Object value = tuple3.getV2();
                if (notDefaultProperty.contains(property)) {
                    continue;
                }
                for (JLTuple.Tuple3<String, Object, Class<?>> tuple3r : tuple3sr) {
                    if (property.equals(tuple3r.getV1())) {
                        defaultArithmetics.add(new JLTuple.Tuple3<>(property, defaults, value));
                        break;
                    }
                }
            }
            arithmetic(queryWrapper, defaultArithmetics);
            return queryWrapper;
        }

        private void set(JLLambda.JLFunction<T, ?> jlFunction, Object value, String way) {
            String property = JLLambda.getProperty(jlFunction);
            JLTuple.Tuple3<String, String, Object> tuple3 = new JLTuple.Tuple3<>(property, way, value);
            arithmetics.add(tuple3);
            notDefaultProperty.add(property);
        }

        /**
         * <
         */
        public QueryWrapperOper<T> eq(JLLambda.JLFunction<T, ?> jlFunction, Object value) {
            set(jlFunction, value, EQ);
            return this;
        }

        /**
         * <
         */
        public QueryWrapperOper<T> eq(JLLambda.JLFunction<T, ?> jlFunction) {
            JLTuple.Tuple3<String, Object, Class<?>> tuple3 = JLReflect.PropertyReflect.getProperty(paramEntity, JLLambda.getProperty(jlFunction));
            eq(jlFunction, tuple3.getV2());
            return this;
        }

        /**
         * <
         */
        public QueryWrapperOper<T> lt(JLLambda.JLFunction<T, ?> jlFunction, Object value) {
            set(jlFunction, value, LT);
            return this;
        }

        /**
         * <
         */
        public QueryWrapperOper<T> lt(JLLambda.JLFunction<T, ?> jlFunction) {
            JLTuple.Tuple3<String, Object, Class<?>> tuple3 = JLReflect.PropertyReflect.getProperty(paramEntity, JLLambda.getProperty(jlFunction));
            lt(jlFunction, tuple3.getV2());
            return this;
        }

        /**
         * >
         */
        public QueryWrapperOper<T> gt(JLLambda.JLFunction<T, ?> jlFunction, Object value) {
            set(jlFunction, value, GT);
            return this;
        }

        /**
         * >
         */
        public QueryWrapperOper<T> gt(JLLambda.JLFunction<T, ?> jlFunction) {
            JLTuple.Tuple3<String, Object, Class<?>> tuple3 = JLReflect.PropertyReflect.getProperty(paramEntity, JLLambda.getProperty(jlFunction));
            gt(jlFunction, tuple3.getV2());
            return this;
        }

        /**
         * <=
         */
        public QueryWrapperOper<T> le(JLLambda.JLFunction<T, ?> jlFunction, Object value) {
            set(jlFunction, value, LE);
            return this;
        }

        /**
         * <=
         */
        public QueryWrapperOper<T> le(JLLambda.JLFunction<T, ?> jlFunction) {
            JLTuple.Tuple3<String, Object, Class<?>> tuple3 = JLReflect.PropertyReflect.getProperty(paramEntity, JLLambda.getProperty(jlFunction));
            le(jlFunction, tuple3.getV2());
            return this;
        }

        /**
         * >=
         */
        public QueryWrapperOper<T> ge(JLLambda.JLFunction<T, ?> jlFunction, Object value) {
            set(jlFunction, value, GE);
            return this;
        }

        /**
         * >=
         */
        public QueryWrapperOper<T> ge(JLLambda.JLFunction<T, ?> jlFunction) {
            JLTuple.Tuple3<String, Object, Class<?>> tuple3 = JLReflect.PropertyReflect.getProperty(paramEntity, JLLambda.getProperty(jlFunction));
            ge(jlFunction, tuple3.getV2());
            return this;
        }

        /**
         * like
         */
        public QueryWrapperOper<T> like(JLLambda.JLFunction<T, ?> jlFunction, Object value) {
            set(jlFunction, value, LIKE);
            return this;
        }

        /**
         * like
         */
        public QueryWrapperOper<T> like(JLLambda.JLFunction<T, ?> jlFunction) {
            JLTuple.Tuple3<String, Object, Class<?>> tuple3 = JLReflect.PropertyReflect.getProperty(paramEntity, JLLambda.getProperty(jlFunction));
            like(jlFunction, tuple3.getV2());
            return this;
        }

        /**
         * ne
         */
        public QueryWrapperOper<T> ne(JLLambda.JLFunction<T, ?> jlFunction, Object value) {
            set(jlFunction, value, NE);
            return this;
        }

        /**
         * ne
         */
        public QueryWrapperOper<T> ne(JLLambda.JLFunction<T, ?> jlFunction) {
            JLTuple.Tuple3<String, Object, Class<?>> tuple3 = JLReflect.PropertyReflect.getProperty(paramEntity, JLLambda.getProperty(jlFunction));
            ne(jlFunction, tuple3.getV2());
            return this;
        }

        /**
         * in
         */
        public QueryWrapperOper<T> in(JLLambda.JLFunction<T, ?> jlFunction, List<?> value) {
            set(jlFunction, value, IN);
            return this;
        }

        /**
         * in
         */
        public QueryWrapperOper<T> in(JLLambda.JLFunction<T, ?> jlFunction) {
            JLTuple.Tuple3<String, Object, Class<?>> tuple3 = JLReflect.PropertyReflect.getProperty(paramEntity, JLLambda.getProperty(jlFunction));
            if (tuple3.getV2() != null) {
                in(jlFunction, (List) tuple3.getV2());
            }
            return this;
        }

        /**
         * notIn
         */
        public QueryWrapperOper<T> notIn(JLLambda.JLFunction<T, ?> jlFunction, List<?> value) {
            set(jlFunction, value, NOT_IN);
            return this;
        }

        /**
         * notIn
         */
        public QueryWrapperOper<T> notIn(JLLambda.JLFunction<T, ?> jlFunction) {
            JLTuple.Tuple3<String, Object, Class<?>> tuple3 = JLReflect.PropertyReflect.getProperty(paramEntity, JLLambda.getProperty(jlFunction));
            if (tuple3.getV2() != null) {
                notIn(jlFunction, (List) tuple3.getV2());
            }
            return this;
        }

        /**
         * 获取构造器
         *
         * @return
         */
        public LambdaQueryWrapper<T> build() {
            //默认
            QueryWrapper<T> queryWrapper = resultClass == null ? defaults(paramEntity) : defaults(paramEntity, resultClass);
            //其他
            queryWrapper = arithmetic(queryWrapper, arithmetics);
            return queryWrapper.lambda();
        }

        /**
         * 其他构造条件处理
         */
        public QueryWrapper<T> arithmetic(QueryWrapper<T> queryWrapper, List<JLTuple.Tuple3<String, String, Object>> arithmetics) {
            for (JLTuple.Tuple3<String, String, Object> arithmeticObj : arithmetics) {
                String property = arithmeticObj.getV1();
                String arithmetic = arithmeticObj.getV2();
                Object value = arithmeticObj.getV3();
                if (paramCheck(value)) {
                    continue;
                }
                if (arithmetic.equals(EQ)) {
                    queryWrapper.eq(humpToLine(property), value);
                    continue;
                }
                if (arithmetic.equals(LT)) {
                    queryWrapper.lt(humpToLine(property), value);
                    continue;
                }
                if (arithmetic.equals(LT)) {
                    queryWrapper.lt(humpToLine(property), value);
                    continue;
                }
                if (arithmetic.equals(GT)) {
                    queryWrapper.gt(humpToLine(property), value);
                    continue;
                }
                if (arithmetic.equals(LE)) {
                    queryWrapper.le(humpToLine(property), value);
                    continue;
                }
                if (arithmetic.equals(GE)) {
                    queryWrapper.ge(humpToLine(property), value);
                    continue;
                }
                if (arithmetic.equals(LIKE)) {
                    queryWrapper.like(humpToLine(property), value);
                    continue;
                }
                if (arithmetic.equals(NE)) {
                    queryWrapper.ne(humpToLine(property), value);
                    continue;
                }
                if (arithmetic.equals(IN)) {
                    if (value instanceof List) {
                        List list = (List) value;
                        if (list.size() == 0) {
                            continue;
                        }
                        queryWrapper.in(humpToLine(property), list);
                    }
                    continue;
                }
                if (arithmetic.equals(NOT_IN)) {
                    if (value instanceof List) {
                        List list = (List) value;
                        if (list.size() == 0) {
                            continue;
                        }
                        queryWrapper.notIn(humpToLine(property), list);
                    }
                    continue;
                }
            }
            return queryWrapper;
        }

        /**
         * 驼峰转下划线
         *
         * @param str
         * @return
         */
        private String humpToLine(String str) {
            Matcher matcher = humpPattern.matcher(str);
            StringBuffer sb = new StringBuffer();
            while (matcher.find()) {
                matcher.appendReplacement(sb, "_" + matcher.group(0).toLowerCase());
            }
            matcher.appendTail(sb);
            return sb.toString();
        }

        /**
         * 参数校验
         */
        private boolean paramCheck(Object value) {
            if (value == null) {
                return true;
            }
            if (value instanceof String) {
                if ("".equals(value.toString().trim())) {
                    return true;
                }
            }
            return false;
        }
    }
}